import json
import os
import socket
import struct
from typing import Optional, Type

from app.core.conf import ST
from loguru import logger


class AGVException(Exception):
    def __init__(self, message):
        self.message = message
        super().__init__(self.message)


class AGV:
    """_summary_

    Returns:
        _type_: _description_
    """

    __slots__ = ("socket_pool", "ip", "port_list", "data")
    _agv_instances = {}

    def __init__(self, ip):
        self.ip = ip
        self.port_list = {"status": 19204, "control": 19205, "nav": 19206, "config": 19207, "other": 19210, "unknown": 19208}
        self.socket_pool = {}
        self.init_socket_pool()
        self.data = None

    def init_socket_pool(self):
        """_summary_

        Raises:
            AGVException: _description_
        """
        for name, port in self.port_list.items():
            try:
                so = self.connect(port)
                self.socket_pool[name] = so
            except Exception as e:
                AGV.rm_socket_by_ip(self.ip)
                raise AGVException(f"{name}:{port}连接失败") from e

    def send(self, name, req_id, msg_type, msg=None):
        """_summary_
        Args:
            name (_type_): _description_
            req_id (_type_): _description_
            msg_type (_type_): _description_
            msg (_type_, optional): _description_. Defaults to None.
        """
        if msg is None:
            msg = {}
        raw_msg = AGV.pack_msg(req_id, msg_type, msg)
        try:
            self.socket_pool.get(name).send(raw_msg)
        except Exception as e:
            logger.error("向AGV发送消息失败,可能5G网络断开,也可能是通信延迟")
            AGV.rm_socket_by_ip(self.ip)
            raise AGVException("向AGV发送消息失败") from e

    def receive(self, name):
        """_summary_
        Args:
            name (_type_): _description_
        Returns:
            _type_: _description_
        """
        so = self.socket_pool.get(name)
        data_all = b""
        try:
            self.data = so.recv(16)  # 接受16个字节的数据
            so.settimeout(5)
        except socket.timeout as e:
            AGV.rm_socket_by_ip(self.ip)
            logger.error("16byte接受超时")
            raise AGVException("16byte接受超时") from e

        if len(self.data) < 16:  # 0-15,刚好16
            AGV.rm_socket_by_ip(self.ip)
            logger.error("pack head error")
            raise AGVException("header小于16字节")
        json_data_len = 0
        try:
            header = struct.unpack("!BBHLH6s", self.data)
        except Exception as e:
            AGV.rm_socket_by_ip(self.ip)
            logger.debug("header无法解包")
            raise AGVException("header无法解包") from e

        json_data_len = header[3]
        data_all += self.data
        data = b""
        read_size = 1024
        try:
            while json_data_len > 0:
                recv = so.recv(read_size)
                data += recv
                json_data_len -= len(recv)
                if json_data_len < read_size:
                    read_size = json_data_len
            data_all += data
            return json.loads(data_all[16:].decode())
        except socket.timeout as e:
            AGV.rm_socket_by_ip(self.ip)
            raise AGVException("msg接受超时") from e

    @staticmethod
    def pack_msg(req_id, msg_type, msg=None):
        """_summary_
        Args:
            req_id (_type_): _description_
            msg_type (_type_): _description_
            msg (_type_, optional): _description_. Defaults to None.
        Returns:
            _type_: _description_
        """
        if msg is None:
            msg = {}

        json_str = json.dumps(msg)
        msg_len = len(json_str) if msg else 0
        raw_msg = struct.pack("!BBHLH6s", 0x5A, 0x01, req_id, msg_len, msg_type, b"\x00\x00\x00\x00\x00\x00")
        if msg_len > 0:
            raw_msg += bytearray(json_str, "ascii")
        return raw_msg

    def connect(self, port: int) -> Optional[socket.socket]:
        """_summary_
        Args:
            port (_type_): _description_
        Returns:
            _type_: _description_
        """
        try:
            so = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            so.connect((self.ip, port))
            so.settimeout(1)
            return so
        except Exception as e:
            AGV.rm_socket_by_ip(self.ip)
            logger.error(f"{self.ip}:{port} fail!!!")
            raise AGVException("{self.ip}:{port} fail!!!") from e

    @classmethod
    def ping_agv(cls, ip):
        exit_code = os.system(f"ping {ip}")
        if exit_code == 0:
            logger.info(f"{ip}5G网络畅通")
            return True
        else:
            return False

    @classmethod
    def get_agv(cls, ip) -> Type["AGV"]:
        """_summary_
        Returns:
            _type_: _description_
        """
        if cls._agv_instances.get(ip) is None:
            try:
                cls._agv_instances[ip] = cls(ip)
            except Exception as e:
                logger.debug(f"{ip}实例创建失败")
                AGV.rm_socket_by_ip(ip)
                raise AGVException(f"{ip}实例创建失败") from e
        return cls._agv_instances.get(ip)

    @classmethod
    def rm_socket_by_ip(cls, ip):
        if ip in cls._agv_instances:
            del cls._agv_instances[ip]
        else:
            logger.error(f"{ip}_已经删除")


def get_test_param():
    with open(ST.AGV_TEST_PATH, "r", encoding="utf-8") as f:
        data = json.load(f)
        return data
